/*
 * BitBoardOps.c
 *
 *  Created on: 2012-11-26
 *      Author: Larry.Li
 */
#include "BitBoard.h"
#include "BitBoardOps.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

BitBoard bitBoardAND(BitBoard *leftOperand, BitBoard *rightOperand) {
	assert(leftOperand != NULL && rightOperand != NULL);
	BitBoard result;
	result.startingBits = leftOperand->startingBits
			& rightOperand->startingBits;
	result.middleBits = leftOperand->middleBits & rightOperand->middleBits;
	result.endingBits = leftOperand->endingBits & rightOperand->endingBits;
	return result;
}

BitBoard bitBoardOR(BitBoard *leftOperand, BitBoard *rightOperand) {
	assert(leftOperand != NULL && rightOperand != NULL);
	BitBoard result;
	result.startingBits = leftOperand->startingBits
			| rightOperand->startingBits;
	result.middleBits = leftOperand->middleBits | rightOperand->middleBits;
	result.endingBits = leftOperand->endingBits | rightOperand->endingBits;
	return result;
}

BitBoard bitBoardXOR(BitBoard *leftOperand, BitBoard *rightOperand) {
	assert(leftOperand != NULL && rightOperand != NULL);
	BitBoard result;
	result.startingBits = leftOperand->startingBits
			^ rightOperand->startingBits;
	result.middleBits = leftOperand->middleBits ^ rightOperand->middleBits;
	result.endingBits = leftOperand->endingBits ^ rightOperand->endingBits;
	return result;
}

BitBoard getFileBitBoardFromLowest10Bits(unsigned long bits, int fileIndex) {
	static const unsigned long mask = 0x1;
	int i;
	BitBoard result = { .startingBits = 0, .middleBits = 0, .endingBits = 0 };
	for (i = 0; i < 10; i++) {
		if ((bits >> i) & mask) {
			BITBOARD_OR(result, fileMask[i]);
		}
	}
	bitBoardLeftShift(&result, fileIndex);
	return result;
}

BitBoard getRankBitBoardFromLowest9Bits(unsigned long bits, int rankIndex) {
	BitBoard result = { .startingBits = bits, .middleBits = 0, .endingBits = 0 };
	bitBoardLeftShift(&result, rankIndex * 9);
	return result;
}

int isBitBoardEmpty(BitBoard *board) {
	assert(board != NULL);
	return (board->startingBits == 0) && (board->middleBits == 0)
			&& (board->endingBits == 0);
}
/**
 * bits from 0 to 89
 */
void bitBoardLeftShift(BitBoard *bitBoard, int bits) {
	assert(bits >= 0 && bitBoard != NULL);
	if (bits >= 90) {
		//fellow the c convention
		bits = bits % 90;
	}
	if (bits == 0)
		return;
	int restBits;
	if (bits > 64) {
		bits = bits - 64;
		restBits = 32 - bits;
		bitBoard->endingBits = bitBoard->startingBits << bits;
		//makes sure the endingBits most 6 bits are 0 coz we just need 32+32+26 bits
		bitBoard->endingBits &= 0x3FFFFFF;
		bitBoard->startingBits = 0;
		bitBoard->middleBits = 0;
	} else if (bits == 64) {
		bitBoard->endingBits = bitBoard->startingBits;
		bitBoard->middleBits = 0;
		bitBoard->startingBits = 0;
	} else if (bits > 32) {
		bits = bits - 32;
		restBits = 32 - bits;
		unsigned long endingBits = (bitBoard->middleBits << bits)
				| (bitBoard->startingBits >> restBits);
		bitBoard->middleBits = bitBoard->startingBits << bits;
		bitBoard->startingBits = 0;
		bitBoard->endingBits = endingBits;
	} else if (bits == 32) {
		bitBoard->endingBits = bitBoard->middleBits;
		bitBoard->middleBits = bitBoard->startingBits;
		bitBoard->startingBits = 0;
	} else {
		restBits = 32 - bits;
		unsigned long startingRestLeastBits = bitBoard->startingBits
				>> restBits;
		unsigned long middleBits = (bitBoard->middleBits << bits)
				| startingRestLeastBits;
		unsigned long middleRestLeastBits = bitBoard->middleBits >> restBits;
		bitBoard->startingBits <<= bits;
		bitBoard->middleBits = middleBits;
		bitBoard->endingBits = (bitBoard->endingBits << bits)
				| middleRestLeastBits;
	}
}

int count32(unsigned long arg) {
	arg = ((arg >> 1) & 0x55555555) + (arg & 0x55555555);
	arg = ((arg >> 2) & 0x33333333) + (arg & 0x33333333);
	arg = ((arg >> 4) & 0x0f0f0f0f) + (arg & 0x0f0f0f0f);
	arg = ((arg >> 8) & 0x00ff00ff) + (arg & 0x00ff00ff);
	return (arg >> 16) + (arg & 0x0000ffff);
}

int lsb32(unsigned long arg) {
	int RetVal = 31;
	if (arg & 0x0000ffff) {
		RetVal -= 16;
		arg &= 0x0000ffff;
	}
	if (arg & 0x00ff00ff) {
		RetVal -= 8;
		arg &= 0x00ff00ff;
	}
	if (arg & 0x0f0f0f0f) {
		RetVal -= 4;
		arg &= 0x0f0f0f0f;
	}
	if (arg & 0x33333333) {
		RetVal -= 2;
		arg &= 0x33333333;
	}
	if (arg & 0x55555555)
		RetVal -= 1;
	return RetVal;
}

int msb32(unsigned long arg) {
	int RetVal = 0;
	if (arg & 0xffff0000) {
		RetVal += 16;
		arg &= 0xffff0000;
	}
	if (arg & 0xff00ff00) {
		RetVal += 8;
		arg &= 0xff00ff00;
	}
	if (arg & 0xf0f0f0f0) {
		RetVal += 4;
		arg &= 0xf0f0f0f0;
	}
	if (arg & 0xcccccccc) {
		RetVal += 2;
		arg &= 0xcccccccc;
	}
	if (arg & 0xaaaaaaaa)
		RetVal += 1;
	return RetVal;
}

int countBitBoardTotalBits(BitBoard *bitBoard) {
	return count32(bitBoard->startingBits) + count32(bitBoard->middleBits)
			+ count32(bitBoard->endingBits);
}
int bitBoardLSB(BitBoard *bitBoard) {
	assert(bitBoard != NULL);
	if (bitBoard->startingBits)
		return lsb32(bitBoard->startingBits);
	if (bitBoard->middleBits)
		return lsb32(bitBoard->middleBits) + 32;
	if (bitBoard->endingBits)
		return lsb32(bitBoard->endingBits) + 64;
	return -1;
}
int bitBoardMSB(BitBoard *bitBoard) {
	assert(bitBoard != NULL);
	if (bitBoard->endingBits)
		return msb32(bitBoard->endingBits) + 64;
	if (bitBoard->middleBits)
		return msb32(bitBoard->middleBits) + 32;
	if (bitBoard->startingBits)
		return msb32(bitBoard->startingBits);
	return -1;
}
unsigned long getBitBoardBitsOfRank(int rankNum, BitBoard *board) {
	assert(rankNum >= 0 && rankNum <= 9);
	unsigned long result;
	if (rankNum < 3) {
		result = (board->startingBits >> (9 * rankNum)) & 0x1FF;
	} else if (rankNum == 3) {
		result = ((board->middleBits & 0xF) << 5) | board->startingBits >> 27;
	} else if (rankNum < 7) {
		result = (board->middleBits >> (9 * (rankNum - 4) + 4)) & 0x1FF;
	} else if (rankNum == 7) {
		result = ((board->endingBits & 0xFF) << 1) | board->middleBits >> 31;
	} else if (rankNum < 10) {
		result = (board->endingBits >> (9 * (rankNum - 8) + 8)) & 0x1FF;
	}
	return result;
}
unsigned long getRotatedBitBoardBitsOfRank(int rankNum, BitBoard *board) {
	assert(rankNum >= 0 && rankNum <= 8);
	unsigned long result;
	if (rankNum < 3) {
		result = (board->startingBits >> (10 * rankNum)) & 0x3FF;
	} else if (rankNum == 3) {
		result = ((board->middleBits & 0xFF) << 2) | board->startingBits >> 30;
	} else if (rankNum < 6) {
		result = (board->middleBits >> (10 * (rankNum - 4) + 8)) & 0x3FF;
	} else if (rankNum == 6) {
		result = ((board->endingBits & 0x3F) << 4) | board->middleBits >> 28;
	} else if (rankNum < 9) {
		result = (board->endingBits >> (10 * (rankNum - 7) + 6)) & 0x3FF;
	}
	return result;
}

void setChessPosition(int chess, int thisPosition, ChessPosition *allPosition) {
	assert((chess <= 7) && (chess >= -7) && (chess != 0));
	assert((thisPosition < 90) && (thisPosition >= 0));
	assert(allPosition != NULL);
	switch (chess) {
	case RB:
		BITBOARD_OR(allPosition->r_bing, mask[thisPosition]);
		break;
	case RS:
		BITBOARD_OR(allPosition->r_shi, mask[thisPosition]);
		break;
	case RX:
		BITBOARD_OR(allPosition->r_xiang, mask[thisPosition]);
		break;
	case RP:
		BITBOARD_OR(allPosition->r_pao, mask[thisPosition]);
		break;
	case RM:
		BITBOARD_OR(allPosition->r_ma, mask[thisPosition]);
		break;
	case RC:
		BITBOARD_OR(allPosition->r_che, mask[thisPosition]);
		break;
	case RJ:
		BITBOARD_OR(allPosition->r_jiang, mask[thisPosition]);
		break;
	case BB:
		BITBOARD_OR(allPosition->b_bing, mask[thisPosition]);
		break;
	case BS:
		BITBOARD_OR(allPosition->b_shi, mask[thisPosition]);
		break;
	case BX:
		BITBOARD_OR(allPosition->b_xiang, mask[thisPosition]);
		break;
	case BP:
		BITBOARD_OR(allPosition->b_pao, mask[thisPosition]);
		break;
	case BM:
		BITBOARD_OR(allPosition->b_ma, mask[thisPosition]);
		break;
	case BC:
		BITBOARD_OR(allPosition->b_che, mask[thisPosition]);
		break;
	case BJ:
		BITBOARD_OR(allPosition->b_jiang, mask[thisPosition]);
		break;
	}
	if (chess > 0) {
		BITBOARD_OR(allPosition->r_pieces, mask[thisPosition]);
	} else {
		BITBOARD_OR(allPosition->b_pieces, mask[thisPosition]);
	}
	BITBOARD_OR(allPosition->all_pieces, mask[thisPosition]);
}

void unsetChessPosition(int thisPosition, ChessPosition *allPosition) {
	assert((thisPosition < 90) && (thisPosition >= 0));
	assert(allPosition != NULL);
	BITBOARD_ANDNOT(allPosition->r_bing, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->r_shi, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->r_xiang, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->r_pao, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->r_ma, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->r_che, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->r_jiang, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_bing, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_shi, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_xiang, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_pao, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_ma, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_che, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_jiang, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->r_pieces, mask[thisPosition]);
	BITBOARD_ANDNOT(allPosition->b_pieces, mask[thisPosition]);

	BITBOARD_ANDNOT(allPosition->all_pieces, mask[thisPosition]);
}

